package cn.hamster3.mc.plugin.core.bukkit.page.handler;

import cn.hamster3.mc.plugin.core.bukkit.page.ButtonGroup;
import cn.hamster3.mc.plugin.core.bukkit.page.PageConfig;
import lombok.Getter;
import lombok.Setter;
import me.clip.placeholderapi.PlaceholderAPI;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.Sound;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 支持翻页的 GUI
 *
 * @param <E> 页面元素
 */
@SuppressWarnings("unused")
public abstract class PageableHandler<E> extends FixedPageHandler {
    private static final ItemStack EMPTY_STACK = new ItemStack(Material.AIR);
    private final HashMap<Integer, E> elementSlot;
    @Setter
    private String previewButtonName = "preview";
    @Setter
    private String nextButtonName = "next";
    @Setter
    private String barrierButtonName = "barrier";
    @Setter
    @Getter
    private String elementButtonName = "element";
    @Setter
    @Getter
    private int page;

    public PageableHandler(@NotNull HumanEntity player) {
        super(player);
        this.page = 0;
        elementSlot = new HashMap<>();
    }

    public PageableHandler(@NotNull HumanEntity player, int page) {
        super(player);
        this.page = page;
        elementSlot = new HashMap<>();
    }

    public PageableHandler(@NotNull PageConfig config, @NotNull HumanEntity player, int page) {
        super(config, player);
        this.page = page;
        elementSlot = new HashMap<>();
    }

    @NotNull
    public abstract List<E> getPageElements();

    public abstract void onClickElement(@NotNull ClickType clickType, @NotNull InventoryAction action, @NotNull E element);

    @NotNull
    public String getElementButtonName(@NotNull E element) {
        return elementButtonName;
    }

    @Override
    public void onClickInside(@NotNull InventoryClickEvent event) {
        event.setCancelled(true);
        int slot = event.getSlot();
        E e = elementSlot.get(slot);
        if (e != null) {
            onClickElement(event.getClick(), event.getAction(), e);
            return;
        }
        String name = getPageConfig().getButtonName(event.getCurrentItem());
        if (name.equalsIgnoreCase(nextButtonName)) {
            showNextPage();
            return;
        }
        if (name.equalsIgnoreCase(previewButtonName)) {
            showPreviewPage();
        }
    }

    @Override
    public void onPlayButtonSound(@NotNull ClickType clickType, @NotNull InventoryAction action, int index) {
        HumanEntity player = getPlayer();
        if (!(player instanceof Player)) {
            return;
        }
        PageConfig config = getPageConfig();
        String buttonName = getButtonGroup().getButtonName(index);
        Sound sound = config.getButtonSound(buttonName);
        if (sound == null) {
            switch (buttonName) {
                case "empty":
                case "barrier": {
                    break;
                }
                default: {
                    sound = config.getButtonSound("default");
                    break;
                }
            }
        }
        if (buttonName.equals("element") && elementSlot.get(index) == null) {
            sound = null;
        }
        if (sound == null) {
            return;
        }
        ((Player) player).playSound(player.getLocation(), sound, 1, 1);
    }

    @Override
    public void initPage() {
        super.initPage();
        List<E> elements = getPageElements();
        ButtonGroup group = getButtonGroup();
        Inventory inventory = getInventory();
        HumanEntity player = getPlayer();

        ArrayList<Integer> buttonIndexes = group.getButtonAllIndex(elementButtonName);
        int pageSize = buttonIndexes.size(); // 一页有多少个按钮
        elementSlot.clear();

        HashMap<String, String> variables = getPageVariables();

        for (int i = 0; i < pageSize; i++) {
            // 元素在当前 page 中的索引位置
            int elementIndex = page * pageSize + i;
            // 按钮在 GUI 中的索引位置
            int buttonIndex = buttonIndexes.get(i);

            if (elementIndex >= elements.size()) {
                inventory.setItem(buttonIndex, null);
                continue;
            }

            E element = elements.get(elementIndex);
            elementSlot.put(buttonIndex, element);

            String buttonName = getElementButtonName(element);
            ItemStack buttonItem = group.getButtonItem(buttonName);
            initElementButton(buttonIndex, element, buttonItem, variables);
        }

        if (page == 0) {
            // 如果页面已在首页则撤掉上一页按钮
            inventory.setItem(group.getButtonIndex(previewButtonName), group.getButtonItem(barrierButtonName));
        }
        if (elements.size() <= (page + 1) * pageSize) {
            // 如果页面显示超出已有元素数量则撤掉下一页按钮
            inventory.setItem(group.getButtonIndex(nextButtonName), group.getButtonItem(barrierButtonName));
        }
    }

    public void initElementButton(int index, @NotNull E element, @NotNull ItemStack stack, @NotNull Map<String, String> variables) {
        ItemMeta meta = stack.getItemMeta();
        if (meta == null) {
            return;
        }
        HumanEntity player = getPlayer();
        if (meta.hasDisplayName()) {
            String displayName = meta.getDisplayName();
            for (Map.Entry<String, String> entry : variables.entrySet()) {
                displayName = displayName.replace(entry.getKey(), entry.getValue());
            }
            if (player instanceof Player && Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) {
                displayName = PlaceholderAPI.setPlaceholders((OfflinePlayer) player, displayName);
            }
            meta.setDisplayName(displayName);
        }
        List<String> lore = meta.getLore();
        if (lore != null) {
            if (player instanceof Player && Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) {
                lore.replaceAll(s -> {
                    for (Map.Entry<String, String> entry : variables.entrySet()) {
                        s = s.replace(entry.getKey(), entry.getValue());
                        s = PlaceholderAPI.setPlaceholders((OfflinePlayer) player, s);
                    }
                    return s;
                });
            } else {
                lore.replaceAll(s -> {
                    for (Map.Entry<String, String> entry : variables.entrySet()) {
                        s = s.replace(entry.getKey(), entry.getValue());
                    }
                    return s;
                });
            }
            meta.setLore(lore);
        }
        stack.setItemMeta(meta);
        inventory.setItem(index, stack);
    }

    public void showPreviewPage() {
        page--;
        show();
    }

    public void showNextPage() {
        page++;
        show();
    }

    @NotNull
    public HashMap<Integer, E> getElementSlot() {
        return elementSlot;
    }

}
